package com.onpositive.mediawiki.propertyparser;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

import com.onpositive.data.IPropertyConstants;
import com.onpositive.data.units.Unit;
import com.onpositive.data.units.Units;
import com.onpositive.semantic.model.api.meta.IMeta;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.semantic.model.api.property.IPropertyProvider;
import com.onpositive.units.AbstractPreprocessor;
import com.onpositive.units.AbstractValueParser;
import com.onpositive.units.ParsedDimension;
import com.onpositive.units.ParsedValue;
import com.onpositive.wikitemplate.DefaultTemplateResolver;

public class DimensionParser extends AbstractValueParser {

	public DimensionParser() {
		super.id = ParsedDimension.ID;
	}

	@Override
	public ParsedValue parse(String value, IMeta meta, String comment )
	{
		String arr[] = value.split(" ") ;
	
		StringBuilder bld = new StringBuilder() ;
		int i = extractNumber(arr, bld);
		if( i >= arr.length )
			return null ;
		
		
		int s = 0 ;
		int l = arr[i].length() ;
		for( ; s < l ; s++ )
		{
			char ch = arr[i].charAt(s);
			if( Character.isDigit(ch) || ch == '.' || ch == ',' || ch == ' ' )
				continue ;
			
			break ;
		}
		
		String unitName = null;
		StringBuilder unitBuilder = new StringBuilder() ;
		if( s < l ){
			unitBuilder.append( arr[i].substring(s) ) ;
			if( Units.isUnit( unitBuilder.toString() ) )
				unitName = unitBuilder.toString() ;
			
			unitBuilder.append( ' ' ) ;
		}
		
		for( int j = i+1 ; j < arr.length; j++ )
		{ 
			unitBuilder.append( arr[j] ) ;
			if( Units.isUnit( unitBuilder.toString() ) )
				unitName = unitBuilder.toString() ;
			
			unitBuilder.append( ' ' ) ;
		}
		
		double doubleValue = 0.0;
		try {
			doubleValue = NumberFormat.getInstance(Locale.US).parse(bld.toString()).doubleValue();
			
			if ( unitName == null && i < arr.length )
				unitName = arr[i] ;
			
			if( unitName == null )
				return null ;
			
			if (unitName.endsWith(",")) {
				unitName = unitName.substring(0, unitName.length() - 1);
			}
			String unitKind = null ;
			if( meta != null )
				unitKind = meta.getSingleValue( IPropertyConstants.CHAR_UNIT_KIND_LABEL, String.class, null ) ;
			
			Unit unit = Units.getUnitByShortName( unitName, unitKind);
				
			if( unit != null )
				return new ParsedDimension(doubleValue, unit, getDefaultComment( comment ));
				//System.out.println(dimension);
			
		} catch (ParseException e) {			
			
		}
		return null ;
		
	}

	public static int extractNumber(String[] arr, StringBuilder bld)
	{
		boolean gotpoint = false ;
		int i = 0 ;
		for(  ; i < arr.length ; i++ )
		{
			String s = arr[i] ;
			int l_ = s.length();
			boolean goodString = true ;
			for( int j = 0 ; j < l_ ; j++ )
			{
				char ch = s.charAt(j) ;
				if( Character.isDigit(ch) ){
					bld.append( ch ) ;
					continue ;
				}				
				else if( ch == '.' && !gotpoint )
				{
					bld.append( ch ) ;
					gotpoint = true ;
					continue ;
				}
				else if( ch == ',' && !gotpoint ){
					bld.append( ch ) ;
					continue ;
				}
				goodString = false ;
				break ;
			}
			if(!goodString)
				break ;
		}
		return i ;
	}

	@Override
	public AbstractPreprocessor getPreprocessor() {

		return new AbstractPreprocessor() {
			@Override
			public String[] preprocess(String[] param, IPropertyProvider propertyProvider, IMeta meta ) {
				
				if (param == null || param.length < 2 || param[0] == null || param[1] == null)
					return null;
				
				boolean gotAChange = false ;
				String[] param1 = fitUnits(param, propertyProvider, meta);
				if( param1 != null )
				{
					param = param1 ;
					gotAChange = true ;
				}
				
				String value = param[1].trim();				
				String value0 = DefaultTemplateResolver.resolveTemplates(value);
				
				if( !value.equals(value0) )
				{
					param[1] = value0 ;
					gotAChange = true ;
				}		
				
				return gotAChange ? param : null ; 
			}

			public String[] fitUnits(String[] param, IPropertyProvider propertyProvider, IMeta meta) {
				
				String[] resultingParam = new String[2] ;

				String name = param[0].trim();
				String value_ = param[1].trim();

				name = resolveRefs(name);
				String value = resolveRefs(value_);

				IMeta mtd = null ;
				if( propertyProvider != null ){
					IProperty prop = propertyProvider.getProperty(null, name);
					if (prop != null)
						mtd = prop.getMeta();					
				}
				if( mtd == null )
					mtd = meta ;
				
				String unitKind = null ;				
				if ( mtd != null) {
					unitKind = mtd.getSingleValue(
							IPropertyConstants.CHAR_UNIT_KIND_LABEL,
							String.class, null);
					if (unitKind != null && unitKind.length() == 0)
						unitKind = null;
				}			

				boolean gotAChange = false ;
				for (int indN = name.lastIndexOf(' '); indN >= 0; indN = name.lastIndexOf(' ', indN - 1)) {
					String unitName = name.substring(indN + 1).trim();
					if (Units.isUnit(unitName)) {
						name = name.substring(0, indN);

						Unit unit = Units.getUnitByShortName(unitName, unitKind);
						
						if( unit == null )
							continue ;
						unitName = unit.getShortName();

						int indV = value.lastIndexOf(' ');
						if (indV == -1)
							value = attachUnit( value, unitName ) ;
						else {
							String unitName0 = value.substring(indV + 1);
							if (!Units.isUnit(unitName0))
								value = attachUnit( value, unitName ) ;
						}
						
						resultingParam[0] = name.trim();
						resultingParam[1] = value.trim();
						gotAChange = true ;
					}
				}
				if( gotAChange )
					return resultingParam ;

				for (int indN = value.indexOf(' '); indN >= 0; indN = value.indexOf(' ', indN + 1))
				{
					String unitName = value.substring(0, indN).trim();
					if (Units.isUnit(unitName)) {
						Unit unit = Units.getUnitByShortName(unitName, unitKind);
						
						if( unit == null )
							continue ;
						unitName = unit.getShortName();

						String val_tmp = value.substring(indN + 1).trim();
						Double d = null;
						try {
							d = NumberFormat.getInstance(Locale.US).parse(val_tmp).doubleValue();
						} catch (ParseException e) {
						}

						if (d != null)
							value = attachUnit( val_tmp, unitName );

						resultingParam[0] = name.trim();
						resultingParam[1] = value.trim();
						gotAChange = true ;
					}
				}
				if( gotAChange )
					return resultingParam ;
				
				//TODO REVIEW
				int k = value.indexOf(' ');
				if (k >= 0) {
					
					String arr[] = value.split(" ") ;					
					StringBuilder bld = new StringBuilder() ;
					int n = extractNumber(arr, bld);					
					
					StringBuilder unitBuilder = new StringBuilder() ;
					for ( int i = n ; i < arr.length ; i++ )
					{
						unitBuilder.append( arr[i] ) ;
						String unitName = unitBuilder.toString() ;
						unitBuilder.append(" ") ;					
					
						if( !Units.isUnit(unitName) )
							continue ;							
					
						Unit unit = Units.getUnitByShortName( unitName,unitKind );
							
						if( unit == null )
							continue ;
						
						unitName = unit.getShortName();
						
						StringBuilder valBuilder = new StringBuilder() ;
						valBuilder.append( bld.toString() ) ;
						valBuilder.append( " " ) ;
						valBuilder.append( unitName ) ;						
						for( int j = i+1 ; j < arr.length ; j++ ){
							valBuilder.append( " " ) ;
							valBuilder.append( arr[j] ) ;
						}
						String val_tmp = valBuilder.toString().trim() ;

						resultingParam[0] = name.trim();
						resultingParam[1] = val_tmp ;
						gotAChange = true ;
					}					
				}
				if( gotAChange )
					return resultingParam ;
				
				return null;
			}
			

			private String attachUnit(String value, String unitName) {
				
				String arr[] = value.split(" ") ;
				
				StringBuilder bld = new StringBuilder() ;
				int n = extractNumber(arr, bld);
				
				bld.append( " " ) ;
				bld.append( unitName ) ;
				for( int i = n ; i < arr.length ; i++ )
				{
					String s = arr[i] ;
					s = s.trim() ;
					if( s.length() == 0 )
						continue ;
					
					bld.append( " " ) ;
					bld.append( s ) ;											
				}
				
				String result = bld.toString() ;
				return result;
			}

			private String resolveRefs(String str) {
				StringBuilder builder = new StringBuilder();

				int st = 0;
				for (int i = str.indexOf("[["); i >= 0; i = str.indexOf("[[", i))
				{
					int j = str.indexOf("]]", i + 2);
					@SuppressWarnings("unused")
					boolean test = i+2 > j || j < 0 ;
					String fullLinkString = str.substring(i + 2, j).trim();
					int k = fullLinkString.indexOf("|");

					String ref = k >= 0 ? fullLinkString.substring(0, k) : fullLinkString;
					// String cap = k >= 0 ? fullLinkString.substring( k+ 1 ) :
					// null ;

					String repl = null;
					for (int ind = 0; ind < refs.length; ind++)
						if (refs[ind].toLowerCase().equals(ref.toLowerCase())) {
							repl = replacement[ind];
							break;
						}

					if (repl != null) {
						builder.append(str.substring(st, i));
						builder.append(" ");
						builder.append(repl);
						st = j + 2;
					}

					i = j + 2;
				}
				builder.append(str.substring(st, str.length()));
				String resultingString = builder.toString().trim();
				return resultingString;
			}
		};
	}

	private static final String[] refs = new String[] { "Mach number" };
	private static final String[] replacement = new String[] { "Mach" };
	
	

}
